﻿//
// © Algobox Ltd - Benn Eichhorn
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using Algobox.Objects.Finance.Assets.Stock;

namespace Algobox.Feeds.Finance.MarketData
{
    /// <summary>
    /// Holds all loaded market data feeds and police's subscriptions to the feeds
    /// </summary>
    public static class MarketFeedManager
    {

        #region Member Variables

        /// <summary>
        /// Logger used by this class
        /// </summary>        
        private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Default connection timeout in seconds
        /// </summary>
        public const int DefaultConnectionTimeout = 15;
        
        /// <summary>
        /// Mapping of avaliable Market Feeds to market feed adaptors
        /// </summary>
        private static ConcurrentDictionary<string, IMarketFeedAdaptor> _feeds = new ConcurrentDictionary<string, IMarketFeedAdaptor>();

        /// <summary>
        /// Mapping of stock listings to market adaptors
        /// </summary>
        private static ConcurrentDictionary<IStock, IMarketFeedAdaptor> _mapStockListing2MarketFeed = new ConcurrentDictionary<IStock, IMarketFeedAdaptor>();

        #endregion

        //////////

        #region Properties



        #endregion

        //////////

        #region Functions


        public static bool TrySetFeed(IStock stock, string feed)
        {
            IMarketFeedAdaptor newAdaptor;
            // get the new adaptor
            if (!_feeds.TryGetValue(feed, out newAdaptor))
            {
                Log.Error("SetFeed failed as there is no adaptor avaliable for " + feed.ToString());
                return false;
            }

            lock (stock)
            {
                IMarketFeedAdaptor oldAdaptor;
                // are we changing feeds?
                if (_mapStockListing2MarketFeed.TryRemove(stock, out oldAdaptor) && oldAdaptor.GetType() != newAdaptor.GetType())
                {
                    // TODO Remove subscriptions and move them to new adaptor
                    Log.Warn("TODO - Remove subscriptions from old adaptor and add them to the new adaptor");
                }

                // this should always be true
                if (!_mapStockListing2MarketFeed.TryAdd(stock, newAdaptor))
                   Log.Error("Threading issue! Anything accessing this dictionary should have a lock on the key");

            }
            return true;
        }


        public static bool TryGetFeed(IStock stock, out IMarketFeedAdaptor adaptor)
        {
            return _mapStockListing2MarketFeed.TryGetValue(stock, out adaptor);
        }


        public static void AddFeed(IMarketFeedAdaptor adaptor)
        {
            if (!_feeds.TryAdd(adaptor.FeedName, adaptor))
            {
                Log.Error("MarketFeedManager is already managing the {0} feed. Why are you trying to add it again?", adaptor.FeedName);
            }
        }

        public static void RemoveFeed(string feed)
        {
            IMarketFeedAdaptor adaptor;

            if (_feeds.TryRemove(feed, out adaptor))
            {
                if (adaptor.IsStarted)
                    adaptor.Stop();
            }
            else
            {
                Log.Error("Can not remove {0} as this feed is not managed", feed.ToString());
            }
        }

        /// <summary>
        /// Start the market feeds
        /// </summary>
        /// <returns>False when one of the feeds failed to start</returns>
        public static void Start()
        {
            foreach (IMarketFeedAdaptor adaptor in _feeds.Values)
                if (!adaptor.IsStarted)
                    adaptor.Start();
        }


        /// <summary>
        /// Stop the market feeds
        /// </summary>
        /// <returns>False when one of the market feeds failed to stop</returns>
        public static void Stop()
        {
            foreach (IMarketFeedAdaptor adaptor in _feeds.Values)
                if (adaptor.IsStarted)
                    adaptor.Stop();
        }

        public static void SubscribeBid(IStock stock, IMarketUpdateBid callback)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.SubscribeBid(stock, callback);
            else
                Log.Error("SubscribeBid couldn't subscribe to {0} as it has no mapped feed adaptor", stock);
        }

        public static void SubscribeAsk(IStock stock, IMarketUpdateAsk callback)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.SubscribeAsk(stock, callback);
            else
                Log.Error("SubscribeAsk couldn't subscribe to {0} as it has no mapped feed adaptor", stock);
        }

        public static void SubscribeLast(IStock stock, IMarketUpdateLast callback)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.SubscribeLast(stock, callback);
            else
                Log.Error("SubscribeLast couldn't subscribe to {0} as it has no mapped feed adaptor", stock);
        }

        public static void UnsubscribeAsk(IStock stock, IMarketUpdateAsk callback)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.UnsubscribeAsk(stock, callback);
            else
                Log.Error("UnsubscribeBidAsk couldn't unsubscribe to {0} as it has no mapped feed adaptor", stock);
        }


        public static void UnsubscribeBid(IStock stock, IMarketUpdateBid callback)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.UnsubscribeBid(stock, callback);
            else
                Log.Error("UnsubscribeBidAsk couldn't unsubscribe to {0} as it has no mapped feed adaptor", stock);
        }


        public static void UnsubscribeLast(IStock stock, IMarketUpdateLast callback)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.UnsubscribeLast(stock, callback);
            else
                Log.Error("UnsubscribeLast couldn't unsubscribe to {0} as it has no mapped feed adaptor", stock);
        }

        /*
        public static void UnsubscribeDepth(IStock stock)
        {
            IMarketFeedAdaptor adaptor;
            if (TryGetFeed(stock, out adaptor))
                adaptor.UnsubscribeDepth(stock);
            else
                Log.Error("UnsubscribeDepth couldn't unsubscribe to {0} as it has no mapped feed adaptor", stock);
        }
        */


        #endregion

        //////////

        #region Enums



        #endregion
    }
}
